package ua.lviv.franko.solvers.stressanalysis.twoDimention.savestructures.bounderyconditions;

import ua.lviv.franko.femShapeFunction2D.FEMShapeFunction1;
import ua.lviv.franko.integration.GaussQuadrature;
import ua.lviv.franko.integration.IFunction2D;
import ua.lviv.franko.matrix.hash.lu.HashMatrixLUStyle;
import ua.lviv.franko.triangulation.SaveStructures.Node;

public class BounderyConditionStress {

	private static final int		NUMBER_OF_UNKNOWNS	= 2;

	private IFunction2D				fx;
	private IFunction2D				fy;

	private Node					A;
	private Node					B;

	private BounderyConditionType	type;

	private GaussQuadrature			quadrature;

	/**
	 * 
	 * @param type
	 *            type of BC
	 * @param a
	 *            null for dirihlet if dont need to set a value ...
	 * @param b
	 *            null for dirihlet if dont need to set a value ...
	 * @param fx
	 * @param fy
	 * @param quadrature
	 *            for integrating and applying BC;
	 */
	public BounderyConditionStress(BounderyConditionType type, Node a, Node b, IFunction2D fx, IFunction2D fy, GaussQuadrature quadrature) {
		super();
		this.type = type;
		A = a;
		B = b;
		this.fx = fx;
		this.fy = fy;
		this.quadrature = quadrature;
	}

	public boolean apply(HashMatrixLUStyle matrix, double[] vector) {
		switch (type) {
		case DIRIHLET_TYPE:
			if (A != null) {
				applyDirihlet(A, matrix, vector);
			}

			if (B != null) {
				applyDirihlet(B, matrix, vector);
			}

			break;

		case NAYMAN_TYPE:
			Node C = new Node(A.x + B.x, A.y + B.y, false, -1);
			final FEMShapeFunction1 fA = new FEMShapeFunction1(A, B, C);
			fA.setNodeNum(1);
			vector[A.Number * NUMBER_OF_UNKNOWNS] += quadrature.GaussOnGamma(A, B, new IFunction2D() {

				@Override
				public double calculate(double x, double y) {
					return fA.calculate(x, y) * fx.calculate(x, y);
				}

				@Override
				public double calculateDerivatyX(double x, double y) {
					return 0;
				}

				@Override
				public double calculateDerivatyY(double x, double y) {
					return 0;
				}
			});
			vector[A.Number * NUMBER_OF_UNKNOWNS + 1] += quadrature.GaussOnGamma(A, B, new IFunction2D() {

				@Override
				public double calculate(double x, double y) {
					return fA.calculate(x, y) * fy.calculate(x, y);
				}

				@Override
				public double calculateDerivatyX(double x, double y) {
					return 0;
				}

				@Override
				public double calculateDerivatyY(double x, double y) {
					return 0;
				}
			});
			fA.setNodeNum(2);
			vector[B.Number * NUMBER_OF_UNKNOWNS] += quadrature.GaussOnGamma(A, B, new IFunction2D() {

				@Override
				public double calculate(double x, double y) {
					return fA.calculate(x, y) * fx.calculate(x, y);
				}

				@Override
				public double calculateDerivatyX(double x, double y) {
					return 0;
				}

				@Override
				public double calculateDerivatyY(double x, double y) {
					return 0;
				}
			});
			vector[B.Number * NUMBER_OF_UNKNOWNS + 1] += quadrature.GaussOnGamma(A, B, new IFunction2D() {

				@Override
				public double calculate(double x, double y) {
					return fA.calculate(x, y) * fy.calculate(x, y);
				}

				@Override
				public double calculateDerivatyX(double x, double y) {
					return 0;
				}

				@Override
				public double calculateDerivatyY(double x, double y) {
					return 0;
				}
			});
			break;

		}

		return true;
	}

	public Node getA() {
		return A;
	}

	public Node getB() {
		return B;
	}

	public IFunction2D getFx() {
		return fx;
	}

	public IFunction2D getFy() {
		return fy;
	}

	public GaussQuadrature getQuadrature() {
		return quadrature;
	}

	public BounderyConditionType getType() {
		return type;
	}

	public void setA(Node a) {
		A = a;
	}

	public void setB(Node b) {
		B = b;
	}

	public void setFx(IFunction2D fx) {
		this.fx = fx;
	}

	public void setFy(IFunction2D fy) {
		this.fy = fy;
	}

	public void setQuadrature(GaussQuadrature quadrature) {
		this.quadrature = quadrature;
	}

	public void setType(BounderyConditionType type) {
		this.type = type;
	}

	protected void applyDirihlet(Node A, HashMatrixLUStyle matrix, double[] vector) {
		matrix.getJRowIndex(A.Number * NUMBER_OF_UNKNOWNS).clear();
		matrix.getJRowValues(A.Number * NUMBER_OF_UNKNOWNS).clear();
		matrix.getJRowIndex(A.Number * NUMBER_OF_UNKNOWNS + 1).clear();
		matrix.getJRowValues(A.Number * NUMBER_OF_UNKNOWNS + 1).clear();
		matrix.setValue(A.Number * NUMBER_OF_UNKNOWNS, A.Number * NUMBER_OF_UNKNOWNS, 1.0);
		vector[A.Number * NUMBER_OF_UNKNOWNS] = fx.calculate(A.x, A.y);

		matrix.setValue(A.Number * NUMBER_OF_UNKNOWNS + 1, A.Number * NUMBER_OF_UNKNOWNS + 1, 1.0);
		vector[A.Number * NUMBER_OF_UNKNOWNS + 1] = fy.calculate(A.x, A.y);

	}

}
